//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<painter.cpp>
///	@path	~/src/gui/composite/
///	@date	2008/06/04
///	@desc	.

#define _FULLAPI
#include "config/config.h"

#include "gui/composite/composition.h"
#include "gui/composite/painter.h"

namespace xeres {

	// Painter
	Painter::Painter( HDC dc )
		: m_hdc( dc )
		, m_layer( NULL )
		, m_graphics( dc )
	{

		// Set composite mode & quality.
		m_graphics.SetCompositingMode( Gdiplus::CompositingModeSourceOver );
		m_graphics.SetCompositingQuality( Gdiplus::CompositingQualityDefault );
	}
	// Painter
	Painter::Painter( Layer * layer )
		: m_hdc( layer->GetDC() )
		, m_layer( layer )
		, m_graphics( layer->GetBitmap() )
	{
		// Set composite mode & quality.
		m_graphics.SetCompositingMode( Gdiplus::CompositingModeSourceOver );
		m_graphics.SetCompositingQuality( Gdiplus::CompositingQualityDefault );
	}
	// DrawText
	void Painter::DrawText( const ARGB& ctext , RefWeak<StyleFont> fnt , const wchar_t * text , int len , const Rect<int>& rc , const Rect<int>& bound , UINT format )
	{
		m_graphics.Flush( Gdiplus::FlushIntentionSync );

		// Get transformation.
		Gdiplus::Matrix matrix;
		m_graphics.GetTransform( &matrix );

		int x = (int)matrix.OffsetX();
		int y = (int)matrix.OffsetY();

		// Get clip region.
		Gdiplus::Rect clip;
		m_graphics.GetClipBounds( &clip );

		Rect<int> clipbound = make_rect( clip.X , clip.Y , clip.Width , clip.Height );
		clipbound = bound & clipbound;

		if( clipbound.is_empty() )
			return;

		Rect<int> rt( rc );
		rt.offset( Pair<int>( x , y ) );
		clipbound.offset( Pair<int>( x , y ) );

		m_layer->DrawText( ctext , fnt->hfont() , text , len , toRECT(rt) , toRECT(clipbound) , format );
	}

	// DrawText
	void Painter::DrawText( const ARGB& ctext , RefWeak<StyleFont> font , const wchar_t * text , int len , const Rect<int>& bound , const Pair<int>& pos ,
		int * arrangement , TextStyle * style , wchar_t prev )
	{
		m_graphics.Flush( Gdiplus::FlushIntentionSync );

		// Get transformation.
		Gdiplus::Matrix matrix;
		m_graphics.GetTransform( &matrix );

		int x = (int)matrix.OffsetX();
		int y = (int)matrix.OffsetY();

		// Get clip region.
		Gdiplus::Rect clip;
		m_graphics.GetClipBounds( &clip );

		Rect<int> clipbound = make_rect( clip.X , clip.Y , clip.Width , clip.Height );
		clipbound = bound & clipbound;

		if( clipbound.is_empty() )
			return;

 		Pair<int> start = pos + Pair<int>( x , y );
		clipbound.offset( Pair<int>( x , y ) );

		m_layer->DrawText( ctext , font , text , len , toRECT(clipbound) , start , arrangement , style , prev );
	}
	// DrawShadowText
	void Painter::DrawShadowText( const ARGB& ctext , const ARGB& stext , RefWeak<StyleFont> fnt , const wchar_t * text , int len , const Rect<int>& rc , const Rect<int>& bound , UINT format , int cx , int cy )
	{
		m_graphics.Flush( Gdiplus::FlushIntentionSync );

		// Get transformation.
		Gdiplus::Matrix matrix;
		m_graphics.GetTransform( &matrix );

		int x = (int)matrix.OffsetX();
		int y = (int)matrix.OffsetY();

		// Get clip region.
		Gdiplus::Rect clip;
		m_graphics.GetClipBounds( &clip );

		Rect<int> clipbound = make_rect( clip.X , clip.Y , clip.Width , clip.Height );
		clipbound = bound & clipbound;

		if( clipbound.is_empty() )
			return;

		Rect<int> rt( rc );
		rt.offset( Pair<int>( x , y ) );
		clipbound.offset( Pair<int>( x , y ) );

		m_layer->DrawShadowText( ctext , stext , fnt->hfont() , text , len , toRECT(rt) , toRECT(clipbound) , format , cx , cy );
	}
	// GetTextWidth
	Rect<int> Painter::GetTextWidth( RefWeak<StyleFont> fnt , const wchar_t * text , int len , UINT format , bool shadowed , int cx , int cy )
	{
		RECT result = m_layer->GetTextWidth( fnt->hfont() , text , len , format , shadowed , cx , cy );
		return Rect<int>( result.left , result.top , result.right , result.bottom );
	}
	// DrawLayer
	void Painter::DrawLayer( Layer * layer , const Pair<int>& pos , const Rect<int>& bound , int alpha )
	{
		DrawLayer( layer , pos , bound , make_rect<int>( 0 , 0 , layer->GetWidth() , layer->GetHeight() ) , alpha );
	}
	// DrawLayer
	void Painter::DrawLayer( Layer * layer , const Pair<int>& pos , const Rect<int>& bound , const Rect<int>& sub , int alpha )
	{
		m_graphics.Flush( Gdiplus::FlushIntentionSync );

		// Get transformation.
		Gdiplus::Matrix matrix;
		m_graphics.GetTransform( &matrix );

		int x = (int)matrix.OffsetX();
		int y = (int)matrix.OffsetY();

		// Get clip region.
		Gdiplus::Rect clip;
		m_graphics.GetClipBounds( &clip );

		Rect<int> clipbound = make_rect( clip.X , clip.Y , clip.Width , clip.Height );
		clipbound = bound & clipbound;

		if( clipbound.is_empty() )
			return;

		Pair<int> start = pos + Pair<int>( x , y );
		clipbound.offset( Pair<int>( x , y ) );

		m_layer->Composite( start , clipbound , layer , sub , alpha );
	}
	// DrawBitmap
	void Painter::DrawBitmap( RefWeak<IBitmap> bitmap , const Pair<int>& pos , const Rect<int>& bound , int alpha , int frame )
	{
		DrawBitmap( bitmap , pos , bound , Rect<int>( 0 , 0 , bitmap->GetSize() ) , alpha , frame );
	}
	// DrawBitmap
	void Painter::DrawBitmap( RefWeak<IBitmap> bitmap , const Pair<int>& pos , const Rect<int>& bound , const Rect<int>& sub , int alpha , int frame /*= 0 */ )
	{
		m_graphics.Flush( Gdiplus::FlushIntentionSync );

		// Get transformation.
		Gdiplus::Matrix matrix;
		m_graphics.GetTransform( &matrix );

		int x = (int)matrix.OffsetX();
		int y = (int)matrix.OffsetY();

		// Get clip region.
		Gdiplus::Rect clip;
		m_graphics.GetClipBounds( &clip );

		Rect<int> clipbound = make_rect( clip.X , clip.Y , clip.Width , clip.Height );
		clipbound = bound & clipbound;

		if( clipbound.is_empty() )
			return;

		Pair<int> start = pos + Pair<int>( x , y );
		clipbound.offset( Pair<int>( x , y ) );

		m_layer->Composite( start , clipbound , bitmap , sub , alpha , frame );
	}
	// BlendFrames
	void Painter::BlendFrames( RefWeak<IBitmap> bitmap , const Pair<int>& pos , const Rect<int>& bound , int alpha , int frame1 , int frame2 , float blend )
	{
		BlendFrames( bitmap , pos , bound , Rect<int>( 0 , 0 , bitmap->GetSize() ) , alpha , frame1 , frame2 , blend );
	}
	// BlendFrames
	void Painter::BlendFrames( RefWeak<IBitmap> bitmap , const Pair<int>& pos , const Rect<int>& bound , const Rect<int>& sub , int alpha , int frame1 , int frame2 , float blend )
	{
		m_graphics.Flush( Gdiplus::FlushIntentionSync );

		// Get transformation.
		Gdiplus::Matrix matrix;
		m_graphics.GetTransform( &matrix );

		int x = (int)matrix.OffsetX();
		int y = (int)matrix.OffsetY();

		// Get clip region.
		Gdiplus::Rect clip;
		m_graphics.GetClipBounds( &clip );

		Rect<int> clipbound = make_rect( clip.X , clip.Y , clip.Width , clip.Height );
		clipbound = bound & clipbound;

		if( clipbound.is_empty() )
			return;

		Pair<int> start = pos + Pair<int>( x , y );
		clipbound.offset( Pair<int>( x , y ) );

		m_layer->BlendFrame( start , clipbound , bitmap , sub , alpha , frame1 , frame2 , blend );
	}
	// Blur
	void Painter::Blur( const Rect<int>& rect , int radius )
	{
		m_graphics.Flush( Gdiplus::FlushIntentionSync );

		// Get transformation.
		Gdiplus::Matrix matrix;
		m_graphics.GetTransform( &matrix );

		int x = (int)matrix.OffsetX();
		int y = (int)matrix.OffsetY();

		// Get clip region.
		Gdiplus::Rect clip;
		m_graphics.GetClipBounds( &clip );

		Rect<int> clipbound = make_rect( clip.X , clip.Y , clip.Width , clip.Height );
		clipbound = rect & clipbound;

		if( clipbound.is_empty() )
			return;

		clipbound.offset( Pair<int>( x , y ) );
		m_layer->Blur( clipbound , radius );
	}

	// DownSample
	void Painter::DownSample( const Rect<int>& rect , int radius )
	{
		m_graphics.Flush( Gdiplus::FlushIntentionSync );

		// Get transformation.
		Gdiplus::Matrix matrix;
		m_graphics.GetTransform( &matrix );

		int x = (int)matrix.OffsetX();
		int y = (int)matrix.OffsetY();

		// Get clip region.
		Gdiplus::Rect clip;
		m_graphics.GetClipBounds( &clip );

		Rect<int> clipbound = make_rect( clip.X , clip.Y , clip.Width , clip.Height );
		clipbound = rect & clipbound;

		if( clipbound.is_empty() )
			return;

		clipbound.offset( Pair<int>( x , y ) );
		m_layer->DownSample( clipbound , radius );
	}
	// StcBitmap
	void Painter::StcBitmap( RefWeak<IBitmap> bitmap , const Pair<int>& pos , const Rect<int>& bound , int alpha , int frame , int sub_w , int sub_h )
	{
		m_graphics.Flush( Gdiplus::FlushIntentionSync );

		// Get transformation.
		Gdiplus::Matrix matrix;
		m_graphics.GetTransform( &matrix );

		int x = (int)matrix.OffsetX();
		int y = (int)matrix.OffsetY();

		// Get clip region.
		Gdiplus::Rect clip;
		m_graphics.GetClipBounds( &clip );

		Rect<int> clipbound = make_rect( clip.X , clip.Y , clip.Width , clip.Height );
		clipbound = bound & clipbound;

		if( clipbound.is_empty() )
			return;

		Pair<int> start = pos + Pair<int>( x , y );
		clipbound.offset( Pair<int>( x , y ) );

		// split bitmap!
		int w = bound.width();
		int h = bound.height();

		int iw = bitmap->GetWidth();
		int ih = bitmap->GetHeight();

		Rect<int> subs[9];
		Rect<int> region[9];

		int short_w = sub_w , short_h = sub_h;
		int long_w = w - 2 * sub_w , long_h = h - 2 * sub_h;

		int s1 = short_w , s2 = short_w + long_w;
		int t1 = short_h , t2 = short_h + long_h;

		int r1 = iw - short_w;
		int r2 = ih - short_h;

		int m1 = r1 - short_w;
		int m2 = r2 - short_h;

		// left,top
		subs[0] = make_rect( 0 , 0 , short_w , short_h );
		region[0] = make_rect( 0 , 0 , short_w , short_h );
		// mid,top
		subs[1] = make_rect( s1 , 0 , long_w , short_h );
		region[1] = make_rect( s1 , 0 , m1 , short_h );
		// right,top
		subs[2] = make_rect( s2 , 0 , short_w , short_h );
		region[2] = make_rect( r1 , 0 , short_w , short_h );

		// left,mid
		subs[3] = make_rect( 0 , t1 , short_w , long_h );
		region[3] = make_rect( 0 , t1 , short_w , m2 );
		// mid,mid
		subs[4] = make_rect( s1 , t1 , long_w , long_h );
		region[4] = make_rect( s1 , t1 , m1 , m2 );
		// right,mid
		subs[5] = make_rect( s2 , t1 , short_w , long_h );
		region[5] = make_rect( r1 , t1 , short_w , m2 );

		// left,bottom
		subs[6] = make_rect( 0 , t2 , short_w , short_h );
		region[6] = make_rect( 0 , r2 , short_w , short_h );
		// mid,bottom
		subs[7] = make_rect( s1 , t2 , long_w , short_h );
		region[7] = make_rect( s1 , r2 , m1 , short_h );
		// right,bottom
		subs[8] = make_rect( s2 , t2 , short_w , short_h );
		region[8] = make_rect( r1 , r2 , short_w , short_h );

		// draw them one by one~

		// 4 corners
		m_layer->Composite( start + subs[0].get_min() , clipbound , bitmap , region[0] , alpha , frame );
		m_layer->Composite( start + subs[2].get_min() , clipbound , bitmap , region[2] , alpha , frame );
		m_layer->Composite( start + subs[6].get_min() , clipbound , bitmap , region[6] , alpha , frame );
		m_layer->Composite( start + subs[8].get_min() , clipbound , bitmap , region[8] , alpha , frame );

		// mid-top
		{
			Rect<int> lb = make_rect( s1 , 0 , long_w , short_h );
			lb.offset( Pair<int>( x , y ) );
			lb = lb & clipbound;
			if( !lb.is_empty() )
			{
				Pair<int> st = start;
				st.x += s1;
				for( int i = s1 ; i < s2 ; i += m1 , st.x += m1 )
				{
					m_layer->Composite( st , lb , bitmap , region[1] , alpha , frame );
				}
			}
		}

		// mid-bottom
		{
			Rect<int> lb = make_rect( s1 , t2 , long_w , short_h );
			lb.offset( Pair<int>( x , y ) );
			lb = lb & clipbound;
			if( !lb.is_empty() )
			{
				Pair<int> st = start;
				st.x += s1;
				st.y += t2;
				for( int i = s1 ; i < s2 ; i += m1 , st.x += m1 )
				{
					m_layer->Composite( st , lb , bitmap , region[7] , alpha , frame );
				}
			}
		}

		// left-mid
		{
			Rect<int> lb = make_rect( 0 , t1 , short_w , long_h );
			lb.offset( Pair<int>( x , y ) );
			lb = lb & clipbound;
			if( !lb.is_empty() )
			{
				Pair<int> st = start;
				st.y += t1;
				for( int i = t1 ; i < t2 ; i += m2 , st.y += m2 )
				{
					m_layer->Composite( st , lb , bitmap , region[3] , alpha , frame );
				}
			}
		}

		// right-mid
		{
			Rect<int> lb = make_rect( s2 , t1 , short_w , long_h );
			lb.offset( Pair<int>( x , y ) );
			lb = lb & clipbound;
			if( !lb.is_empty() )
			{
				Pair<int> st = start;
				st.x += s2;
				st.y += t1;
				for( int i = t1 ; i < t2 ; i += m2 , st.y += m2 )
				{
					m_layer->Composite( st , lb , bitmap , region[5] , alpha , frame );
				}
			}
		}

		// center
		{
			Rect<int> lb = make_rect( s1 , t1 , long_w , long_h );
			lb.offset( Pair<int>( x , y ) );
			lb = lb & clipbound;
			if( !lb.is_empty() )
			{
				for( int iy = t1 ; iy < t2 ; iy += m2 )
				{
					Pair<int> st = start;
					st.x += s1;
					st.y += iy;
					for( int ix = s1 ; ix < s2 ; ix += m1 , st.x += m2 )
					{
						m_layer->Composite( st , lb , bitmap , region[4] , alpha , frame );
					}
				}
			}
		}
	}

} // namespace xeres
